module LogiLang.Query (
	Query(..),
	Database,
	Rule,
 	qeval
	) where

import qualified Data.Map as Map
import qualified Data.Maybe as Maybe

import LogiLang.Environment
import LogiLang.Match

data Query =
	SimpleQuery Pattern 
	| CompoundQuery String [Query]
	deriving (Show)

type Database = [Pattern]

type Rule = (Pattern, Maybe Query)

flatten :: [[Environment]] -> [Environment]
flatten = foldl (++) []   -- TODO: flatten properly for better performance

interleave :: [[Environment]] -> [Environment]
interleave = foldl (++) []   -- TODO: interleave properly for better performance

qeval :: Database -> [Rule] -> Query -> [Environment] -> [Environment]
qeval db rules (SimpleQuery pat) environments =
	flatten [assertions env ++ applied env | env <- environments] 
	where
	assertions env = Maybe.catMaybes $ map (match env pat) db 
	applied env = applyRules db rules env pat

qeval db rules (CompoundQuery conn children) environments = 
	case Map.lookup conn connectives of
		Just connective -> connective db rules children environments
		Nothing -> error ("Invalid Connective: " ++ conn)

type Connective = Database -> [Rule] -> [Query] -> [Environment] -> [Environment]

conjoin db rules conjuncts environments =
	foldl (\envs conjunct -> qeval db rules conjunct envs) environments conjuncts   

disjoin db rules disjuncts environments =
	interleave $ map (\disjunct -> qeval db rules disjunct environments) disjuncts

negation db rules operands environments =
	flatten [[env] | env <- environments, all (\operand -> null (qeval db rules operand [env])) operands]

connectives :: Map.Map String Connective
connectives = Map.fromList [
	("and", conjoin),          
	("or", disjoin),
	("not", negation)          
	]                                       

applyRules :: Database -> [Rule] -> Environment -> Pattern -> [Environment]
applyRules _ [] _ _ = []
applyRules db rules env pattern =
	flatten (map (applyRule db rules env pattern) rules)

applyRule :: Database -> [Rule] -> Environment -> Pattern -> Rule -> [Environment]
applyRule db rules env pattern (conclusion, body) =
	case unify (extend env Map.empty) pattern conclusion of
		Just env' ->
			case body of
				Just query -> qeval db rules query [env']
				Nothing -> [env']
		Nothing -> []


